﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using ChatTools;
using System.Threading;

namespace ChatClient
{
    internal class Client
    {
        private Socket client_socket;
        private const int RECEIVE_BUFF_SIZE = 1024;
        private bool connected = false;

        public event EventHandler<ConnectEventArgs> RefreshConnectStatus = null;

        public event EventHandler<ReceiveEventArgs> DataReceive = null;

        public Client()
        {
            this.client_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        public void Connect(string IPAddress, int Port, int id) => ThreadPool.QueueUserWorkItem((WaitCallback)(_ =>
        {
            this.connected = false;
            while (true)
            {
                if (Tools.IsSocketConnectedServer(this.client_socket))
                {
                    if (!this.connected)
                    {
                        this.connected = true;
                        this.RefreshConnectStatus((object)this, new ConnectEventArgs()
                        {
                            Connected = true
                        });
                        this.client_socket.Send(Encoding.Default.GetBytes(string.Format("{0}|{1}|{2}|{3}", 0, id, "", "")));
                        ReceiveState state = new ReceiveState(this.client_socket, RECEIVE_BUFF_SIZE);
                        this.client_socket.BeginReceive(state.Buffer, 0, RECEIVE_BUFF_SIZE, SocketFlags.None, new AsyncCallback(this.ReceiveCallback), (object)state);
                    }
                }
                else
                {
                    if (this.connected)
                    {
                        this.connected = false;
                        this.RefreshConnectStatus((object)this, new ConnectEventArgs()
                        {
                            Connected = false
                        });
                    }
                    try
                    {
                        this.client_socket.Connect(IPAddress, Port);
                    }
                    catch (SocketException ex)
                    {
                    }
                    catch (InvalidOperationException ex)
                    {
                        this.client_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    }
                }
            }
        }));

        public bool Send(string Text)
        {
            try
            {

                this.client_socket.Send(Encoding.Default.GetBytes(Text));
                return true;
            }
            catch (Exception ex) when (ex is SocketException || ex is ObjectDisposedException)
            {
                return false;
            }
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            ReceiveState asyncState = ar.AsyncState as ReceiveState;
            int len;
            try
            {
                len = asyncState.Socket.EndReceive(ar);
            }
            catch (Exception ex) when (ex is ObjectDisposedException || ex is SocketException)
            {
                return;
            }
            this.ProcessReceivedData(asyncState.Socket, asyncState.Buffer, len);
            asyncState.Socket.BeginReceive(asyncState.Buffer, 0, RECEIVE_BUFF_SIZE, SocketFlags.None, new AsyncCallback(this.ReceiveCallback), (object)asyncState);
        }

        private void ProcessReceivedData(Socket socket, byte[] buffer, int len) => this.DataReceive((object)this, new ReceiveEventArgs()
        {
            Text = Encoding.Default.GetString(buffer, 0, len)
        });
    }
}
